This type of time or event-driven animation takes a little getting used to. When programming this sort of behavior you must remember that it is the event that drives the next animation step, not the other way around. In normal programming the flow of logic would be something like :

for(i = 1 to numSteps) {
  doNextAction();
}
The way things are done in our system would be two independent processes, the event generator and the function it is routed to. Thus,
Process Timer : forever { generateTick() };
Process doNextAction : when (tickReceived) { doStuff };
(all this of course is very pseudo code !)

How is this different ? Well among other things the entry point from the timer is just one place. Suppose in traditional action I wanted to do a sequence of actions like :

forever {
  for(i = 1 to 10) { turnClockwise(9); }
  for(i = 1 to 20) { moveForward(1); }
  for(i = 1 to 10) { turnCounterClockwise(9); }
}
This looped sequence turns an object by 90 degrees in steps of 9, then moves it forward by 20 units, then turns it back. Presumably this system will take one step in each new frame, thus providing a smooth motion.

Doing the same thing in an event-driven system like the CP VRML/Java system requires a little more thought and bookkeeping. In particular, let's assume there is a TimeSensor or other clock which generates ticks which are routed to the method ticked(). Now since we only enter through ticked() we must know what stage of the animation we are in, we must take the correct next step, update the state and detect change to the next stage and termination of the sequence. All these must be tracked through counters and flags. The pseudocode for this is :

initialiser() {
  state = 1;
  i = 0;
}
ticked() {
   switch (state) {
      case 1 : 
        turnClockwise(9);
        i = i + 1;  
        if(i == 11) { // update to next stage
          i = 0;
          state = state + 1;
        }
        break;
      case 2 : 
        moveForward(1);
        i = i + 1;
        if(i == 21) {
          i = 0;
          state = state + 1;
        }
        break;
      case 3 :
       turnCounterClockwise(9);      
       i = i + 1;  
       if(i == 11) { // update to next stage
          i = 0;
          state = 1;
       }
       break;
    }  // end switch
} // end ticked

There are other ways to do this. The same tick can go to three different methods, each of which checks the global state. One of the methods is active and updates the state. The state maintenance and transitions could be done in an Object Oriented way. These are advisable in more complex sequences where the state transitions can become quite complex. The basic principle remains the same.

In dealing with infinite loops like this be very careful not to have counters increase or decrease indefinitely as they may go out of bounds and crash the program. Usually it's better to have cyclic variables : for example if you are tracking total angle rotated :

// BAD !! 
totalAngle = totalAngle + increment;
// GOOD
totalAngle = (totalAngle + increment)%360; // where % is the remainder operator
// OR
totalAngle = (totalAngle + increment);
if(totalAngle >= 360)
  totalAngle = totalAngle - 360;
This will pin the angle from 0 to 360.


Click here to return to the Tutorial Three main page.